/**
 * Copyright (c) 2020 kedacom
 * OpenATC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 * http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 **/
package algorithm;

import com.openatc.model.model.Pattern;
import com.openatc.model.model.Split;
import com.openatc.model.model.StageForStageMode;
import lombok.Data;
import model.RouteIntsection;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

@Data
public class Kdalgorithm {

    private int precycle;  //优化前周期
    private int aftcycle;  //优化后周期
    private double trafficintensity;  //关键路口交通强度值
    private int m_volume;  //关键路口关键车道平均交通流量
    private int m_saturation;  //关键路口关键车道饱和度

    Greenwave gwup = new Greenwave();  //优化后上行带宽参数
    Greenwave gwdown = new Greenwave();//优化后下行带宽参数


    /**
     * 参考 scats模型
     * 根据路口流量，优化周期；
     * <p>
     * volume  流量
     * min1    最小周期参数min1
     * min2    最小流量参数min2
     * volume  流量
     * min1    最小流量参数min1
     * min2    最小流量参数min2
     */

    public int cycleOpt(int volume, int saturation, int cyclemin1, int cyclealt1, int cyclealt2, int cyclestr, int cyclemax) {
        m_volume = volume;
        m_saturation = saturation;

        if (m_volume <= 4)  // 3~5
            aftcycle = cyclemin1;
        else if (m_volume <= 8) // 6~9
            aftcycle = cyclealt1;
        else if (m_saturation <= 85) // 80~90
            aftcycle = cyclealt2;
        else if (m_saturation <= 100) // 90~100
        {
            aftcycle = cyclealt2 + cyclestr;  //待计算
        } else if (m_saturation <= 120) // 110~140
        {
            aftcycle = cyclestr + cyclemax;  //待计算
        } else
            aftcycle = cyclemax;

        //和前一周期相比，差别不能太大

        return aftcycle;
    }

    /**
     * 根据各路段长度、速度、 求解单向绿波相位差,带宽参数；
     * intsoffset[];  各路口相位差
     */
    public int[] offsetOpt(String type, double intslenth[], double intsvel, int intssplit[]) {
        double vel = 0.0; //速度m/s
        vel = intsvel / 3.6;
        int intsoffset[]; //各路口相位差
        intsoffset = new int[intslenth.length];
        int intstraltime[]; //路段平均行程时间   假入10个路口，10个平均行程时间
        intstraltime = new int[intslenth.length];

        for (int i = 0; i < intslenth.length; i++) {
            intsoffset[i] = 0;
        }
        for (int i = 0; i < (intslenth.length); i++) {
            intstraltime[i] = 0;
        }

        //计算每一个路段所需行程时间
        for (int i = 0; i < intslenth.length; i++)
            intstraltime[i] = (int) (intslenth[i] / vel);

        //相位差计算
        for (int i = 0; i < intslenth.length; i++) {
            if (intstraltime[i] >= aftcycle)
                intsoffset[i] = intstraltime[i] % aftcycle;
            else
                intsoffset[i] = intstraltime[i];
        }

        //重新修正为相对第一个路口的相位差
        for (int i = 0; i < intslenth.length; i++) {
            if (i == 0)
                intsoffset[i] = 0;
            else
                intsoffset[i] = (intsoffset[i] + intsoffset[i - 1]) % aftcycle;
        }

        //计算带宽参数 单向的话等于最小的绿信比的值
//        calbandwidth(type,intsvel,intssplit,intsoffset);

        return intsoffset;
    }

    /**
     * 计算带宽参数
     */
    public void calbandwidth(String type,double intsvel,int intssplit[],int intsoffset[]){
        if(intsoffset.length == 0)
            return;

        int width = aftcycle;
        for (int i = 0; i < intssplit.length; i++) {
            if (width > intssplit[i]) //在各路口绿信比内
                width = intssplit[i];
        }
        int vehicle = width / 3;
        if (type.equals("up"))   //up, down
        {
            gwup.start = 0;
            gwup.type = "up";
            gwup.width = width;
            gwup.speed = intsvel;
            gwup.vehicle = vehicle;


        } else if (type.equals("down")) {
            //调整相位差
            for (int i = 0; i < intssplit.length; i++) {
                if (intsoffset[i] != 0)
                    intsoffset[i] = aftcycle - intsoffset[i];
            }

            gwdown.start = 0;
            gwdown.type = "down";
            gwdown.width = width;
            gwdown.speed = intsvel;
            gwdown.vehicle = vehicle;
        }
    }
    /**
     * 根据各路段长度、速度、 图解法 求解双向绿波相位差，带宽参数；
     */
    public int[] offsetByBiDirection(double intslenth[], double intsvelup, double intsveldown, int intssplit[]) {
        //1.确定和第一个路口同步式协调or交互式协调

//        int[] intsmode; //各路口协调方式，0 同步式，cycle/2 交互式
        int[] intsoffset; //各路口相位差
//        intsmode = new int[intslenth.length];
        intsoffset = new int[intslenth.length];

        int[] intstraltime; //路段平均行程时间   假入10个路口，10个平均行程时间, 除以周期后的相对时间,分正反向计算
        intstraltime = new int[intslenth.length];

//        for(int i=0;i<intslenth.length;i++)
//        {
//            intsmode[i]=0;
//        }
        for (int i = 0; i < (intslenth.length - 1); i++) {
            intstraltime[i] = 0;
        }

        gwup.start = 0;
        gwup.type = "up";
        gwup.width = 0;
        gwup.speed = intsvelup;
        gwup.vehicle = 10;

        gwdown.start = 0;
        gwdown.type = "down";
        gwdown.width = 0;
        gwdown.speed = intsveldown;
        gwdown.vehicle = 10;

        //计算每一个路段所需行程时间

//        intsveldown=intsveldown*1000/3600;
        intsvelup = intsvelup * 1000 / 3600;

        int s = 0; //加和得到该路口同前面所有路口的距离;
        for (int i = 0; i < intslenth.length; i++) {
            if (i == 0)
                intstraltime[0] = 0;//基准路口
            else {
                s += intslenth[i];
                intstraltime[i] = (int) (s / intsvelup);
            }
        }

        for (int i = 0; i < intslenth.length; i++) {
            int m_temp = intstraltime[i] % aftcycle;
            if (m_temp <= (aftcycle / 2)) {
//                intsmode[i]=0; //0同步式，aftcycle/2交互式
                intsoffset[i] = 0;
            } else {
//                intsmode[i]=aftcycle/2;
                intsoffset[i] = aftcycle / 2;
            }
        }

        ///////////////////////////////////

        int width = 0;

        //依此调整每个路口的相位差，最后得到整个绿波带的最大值
        for (int i = 0; i < intslenth.length; i++) {

            int offset = intsoffset[i];

            for (int off = 0; off < aftcycle; off++) {
                //求得i路口此相位差下绿波带宽
                int pt1 = -1;
                int pt2 = -1;
                int pt3 = -1;
                int pt4 = -1;
                intsoffset[i] = off;
                //正向第一个点
                for (int j = 0; j < intslenth.length; j++) {
                    //判断点 intsoffset[j]，intsoffset[j]+intssplit[j]是不是绿波带的关键边界点。并统一到第一个路口对应坐标上。
                    int d1 = (1000 * aftcycle + intsoffset[j] - intstraltime[j]) % aftcycle;

                    boolean ff1 = isintsOffsetPointforward(1, d1, intsoffset, intstraltime, intssplit);
                    if (ff1) {
                        pt1 = calFirstPoint(pt1, d1);
                        break;
                    }
                }
                //正向第er个点
                for (int j = 0; j < intslenth.length; j++) {
                    //判断点 intsoffset[j]，intsoffset[j]+intssplit[j]是不是绿波带的关键边界点。并统一到第一个路口对应坐标上。
                    int d2 = (1000 * aftcycle + intsoffset[j] + intssplit[j] - intstraltime[j]) % aftcycle;

                    boolean ff2 = isintsOffsetPointforward(2, d2, intsoffset, intstraltime, intssplit);
                    if (ff2) {
                        pt2 = calTwoPoint(pt2, d2);
                        break;
                    }
                }

                int width1 = pt2 - pt1;

                //反向第一个点
                for (int j = 0; j < intslenth.length; j++) {
                    int d1 = (1000 * aftcycle + intsoffset[j] + intstraltime[j]) % aftcycle;

                    boolean ff1 = isintsOffsetPointreverse(1, d1, intsoffset, intstraltime, intssplit);
                    if (ff1) {
                        pt3 = calFirstPoint(pt3, d1);
                        break;
                    }

                }

                //反向第二个点
                for (int j = 0; j < intslenth.length; j++) {
                    int d2 = (1000 * aftcycle + intsoffset[j] + intssplit[j] + intstraltime[j]) % aftcycle;
                    boolean ff2 = isintsOffsetPointreverse(2, d2, intsoffset, intstraltime, intssplit);
                    if (ff2) {
                        pt4 = calTwoPoint(pt4, d2);
                        break;
                    }
                }

                int width2 = pt4 - pt3;

                if (width < (width1 + width2)) {
                    //记下相位差，正反向第一个路口初始值
                    offset = off;
                    width = width1 + width2;
                    gwup.start = pt1;
                    gwup.width = width1;
                    gwdown.start = pt3;
                    gwdown.width = width2;
                    // 修改车辆数
                    int upVehicle = width1 / 3;
                    int downVehicle = width2 / 3;
                    gwup.vehicle = upVehicle;
                    gwdown.vehicle = downVehicle;
                }
            }

            intsoffset[i] = offset;

        }

        return intsoffset;
    }

    //是否正向端点
    //端点1必须经过的都是下端点；
    //端点2必须经过的都是上端点；
    public boolean isintsOffsetPointforward(int num, int dis, int intsoffset[], int intstraltime[], int intssplit[]) {
        boolean flag = true;

        for (int k = 0; k < intsoffset.length; k++) {
            int temp = (dis + intstraltime[k]) % aftcycle;
            if ((temp < intsoffset[k] && temp >= 0) || temp > (intsoffset[k] + intssplit[k]) || (num == 1 && temp == (intsoffset[k] + intssplit[k])) || (num == 2 && temp == intsoffset[k])) {
                flag = false;
                break;
            }
        }

        return flag;
    }

    //是否反向端点
    public boolean isintsOffsetPointreverse(int num, int dis, int intsoffset[], int intstraltime[], int intssplit[]) {
        boolean flag = true;
        for (int k = 0; k < intsoffset.length; k++) {
            int temp = (1000 * aftcycle + dis - intstraltime[k]) % aftcycle;
            if ((temp < intsoffset[k] && temp >= 0) || temp > (intsoffset[k] + intssplit[k]) || (num == 1 && temp == (intsoffset[k] + intssplit[k])) || (num == 2 && temp == intsoffset[k])) {
                flag = false;
                break;
            }
        }
        return flag;
    }

    //重新计算端点1
    public int calFirstPoint(int pt1, int dis) {
        //判断第一个点是否存在
        if (pt1 >= 0) {
            if (dis < pt1)
                pt1 = dis;
        } else
            pt1 = dis;
        return pt1;
    }

    //重新计算端点2
    public int calTwoPoint(int pt2, int dis) {
        //判断第er个点是否存在
        if (pt2 >= 0) {
            if (dis > pt2)
                pt2 = dis;
        } else
            pt2 = dis;
        return pt2;
    }


    /**
     * 获取公共周期
     * @param intscycle
     * @return
     */
    public int getRewWaveCycle(int[] intscycle) {
        // 参与协调的路口周期应该相同
        int max = intscycle[0];
        for (int i = 1; i < intscycle.length; i++){
            if (intscycle[i] > max){
                max = intscycle[i];
            }
        }
        return max;
    }

    /**
     * 优化后的相位差
     * @param distance
     * @param speed
     * @param intssplit
     * @param cycle
     * @param sortid
     * @param offsets
     * @return
     */
    public int[] motorcadeOffsetOptUp(int distance, double speed, int[] intssplit, int cycle, int sortid, int[] offsets) {
        int offset = 0;
        if (sortid == 1) {
            return offsets;
        }
        int lastIntsSplit = intssplit[sortid - 2]; // 上游路口参与协调的相位绿信比
        double v = speed / 3.6; // km/h --->m/s
        int time = (int) (distance / v);
        // 上游路口协调相位绿信比和行驶时间之和小于公共周期
        if (lastIntsSplit + time < cycle){
            offset = lastIntsSplit + time;  // 上游路口相位绿灯末出发的车辆到达当前路口，当前路口即将绿灯
        }
        // 上游路口协调相位绿信比和行驶时间之和大于公共周期
        else if (lastIntsSplit + time > cycle){
           offset = lastIntsSplit + time - cycle;
        }
        offsets[sortid - 1] = offset + offsets[sortid -2];
        while (offsets[sortid -1] >= cycle){
            offsets[sortid - 1] -= cycle;
        }
        return offsets;

    }

    /**
     * 车队尾协调（上行）
     * @param type
     * @param speed
     * @param width
     * @return
     */
    public Greenwave getMUp(String type, double speed, int width) {
        Greenwave greenwave = new Greenwave();
        greenwave.setStart(0);
        greenwave.setWidth(width);
        greenwave.setVehicle(10);
        greenwave.setType(type);
        greenwave.setSpeed(speed);
        return greenwave;
    }

    /**
     * 车队尾协调（下行）
     * @param type
     * @param speed
     * @param width
     * @return
     */
    public Greenwave getMDown(String type, double speed, int width) {
        Greenwave greenwave = new Greenwave();
        greenwave.setType(type);
        greenwave.setStart(0);
        greenwave.setSpeed(speed);
        greenwave.setWidth(width);
        greenwave.setVehicle(10);
        return greenwave;
    }

    /**
     * 上行车队尾协调带宽
     * @param intssplit
     * @return
     */
    public int getMotorcadeWidth(int[] intssplit) {
        int minWidth = intssplit[0];
        for (int i = 0; i < intssplit.length; i++){
            if (intssplit[i] < minWidth){
                minWidth = intssplit[i];
            }
        }
        return minWidth;
    }

    /**
     * 车队尾协调优化相位差（下行）
     * @param speed
     * @param intssplit
     * @param cycle
     * @param sortid
     * @param offsets
     * @param intsdistance
     * @return
     */
    public int[] motorcadeOffsetOptDown(double speed, int[] intssplit, int cycle, int sortid, int[] offsets, int[] intsdistance) {
        if (sortid == intsdistance.length){
            // 当前路口是最后一个路口
            return offsets;
        }
        int offset = 0;
        int lastIntsSplit = intssplit[sortid]; // 上游路口参与协调的相位绿信比
        double v = speed / 3.6; // km/h --->m/s
        int lastDistance = intsdistance[sortid];
        int time = (int) (lastDistance / v);
        // 上游路口协调相位绿信比和行驶时间之和小于公共周期
        if (lastIntsSplit + time < cycle){
            offset = lastIntsSplit + time;  // 上游路口相位绿灯末出发的车辆到达当前路口，当前路口即将绿灯
        }
        // 上游路口协调相位绿信比和行驶时间之和大于公共周期
        else if (lastIntsSplit + time > cycle){
            offset = lastIntsSplit + time - cycle;
        }
        offsets[sortid - 1] = offset + offsets[sortid];
        while (offsets[sortid -1] >= cycle){
            offsets[sortid - 1] -= cycle;
        }
        return offsets;
    }

    /**
     * 红波协调相位差优化（上行）
     * @param distance
     * @param speed
     * @param intssplit
     * @param cycle
     * @param sortid
     * @param offsets
     * @return
     */
    public int[] redwaveOffsetOptUp(int distance, double speed, int[] intssplit, int cycle, int sortid, int[] offsets) {
        if (sortid == 1) {
            return offsets;
        }
        // 上游路口绿灯起始时出发的车辆到达当前路口，刚好红灯
        int lastIntsSplit = intssplit[sortid - 2]; // 上游路口参与协调的相位绿信比
        // 上个路口的相位差
        int lastIntsOffset = offsets[sortid - 2];
        double v = speed / 3.6; // km/h --->m/s
        // 1、计算当前路口和上个路口红灯启动的时间差
        int redStartOffset = (int)Math.abs(distance / v - lastIntsSplit) % cycle;
        // 2、计算上个路口的红灯启动时间（相位差+绿信比）
        int lastIntsRedStart;
        if (lastIntsOffset + lastIntsSplit > cycle) {
            lastIntsRedStart = lastIntsOffset + lastIntsSplit - cycle;
        } else {
            lastIntsRedStart = lastIntsOffset + lastIntsSplit;
        }
        // 3、计算当前路口红灯启动时间
        int currIntsSplit = intssplit[sortid - 1];  // 当前路口绿信比
        int currIntsRedStart = distance / v < currIntsSplit ? lastIntsRedStart - redStartOffset : lastIntsRedStart + redStartOffset;
        // 4、转换为相位差
        int offset = currIntsRedStart < currIntsSplit ? cycle - (currIntsSplit - currIntsRedStart) : currIntsRedStart - currIntsSplit;
        offsets[sortid - 1] = offset % cycle;
        return offsets;
    }

    /**
     * 红波协调（下行）
     * @param speed
     * @param intssplit
     * @param cycle
     * @param sortid
     * @param offsets
     * @param distance
     * @return
     */
    public int[] redwaveOffsetOptDown(double speed, int[] intssplit, int cycle, int sortid, int[] offsets, int distance) {
//        if (sortid == intsdistance.length){
//            // 当前路口是最后一个路口
//            return offsets;
//        }
        // 上游路口绿灯起始时出发的车辆到达当前路口，刚好红灯
        int lastIntsSplit = intssplit[sortid]; // 上游路口参与协调的相位绿信比
        // 上个路口的相位差
        int lastIntsOffset = offsets[sortid];
        double v = speed / 3.6; // km/h --->m/s
//        int distance = intsdistance[sortid];
        // 1、计算当前路口和上个路口红灯启动的时间差
        int redStartOffset = (int)Math.abs(distance / v - lastIntsSplit) % cycle;
        // 2、计算上个路口的红灯启动时间（相位差+绿信比）
        int lastIntsRedStart;
        if (lastIntsOffset + lastIntsSplit > cycle) {
            lastIntsRedStart = lastIntsOffset + lastIntsSplit - cycle;
        } else {
            lastIntsRedStart = lastIntsOffset + lastIntsSplit;
        }
        // 3、计算当前路口红灯启动时间
        int currIntsSplit = intssplit[sortid - 1];  // 当前路口绿信比
        int currIntsRedStart = distance / v < currIntsSplit ? lastIntsRedStart - redStartOffset : lastIntsRedStart + redStartOffset;
        // 4、转换为相位差
        int offset = currIntsRedStart < currIntsSplit ? cycle - (currIntsSplit - currIntsRedStart) : currIntsRedStart - currIntsSplit;
        offsets[sortid - 1] = offset % cycle;
        return offsets;
    }

    /**
     * 计算绿波协调上行带宽
     *
     * @param intsvel
     * @param intssplit
     * @param intsoffset
     * @param intsdistances
     * @param upDistance
     */
    public void calUpBandwidth(double intsvel, int[] intssplit, int[] intsoffset, double[] intsdistances, int upDistance,int[] intscycle) {
        int cycle = intscycle[0];

        gwup.distance = upDistance;
        gwup.type = "up";
        gwup.speed = intsvel;

        for(int i = 1; i < intscycle.length; i++){
            if( cycle != intscycle[i] )
                return;
        }

        double vel = intsvel / 3.6; // 速度 m/s
        // 计算各路口的绿灯开始时间和结束时间
        // 各路口的绿灯开始时间
        double[] intsGreenStartTime = new double[intssplit.length];
        // 各路口的绿灯结束时间
        double[] intsGreenEndTime = new double[intssplit.length];
        // 上行：从第一个路口绿灯开始到各路口的时间
        double[] intsUpArriveBeginTime = new double[intssplit.length];
        // 上行：从第一个路口绿灯结束到各路口的时间
        double[] intsUpArriveEndTime = new double[intssplit.length];
        // x: 距离   y: 时间(考虑相位差)    y = x/v + b
        // 上行常量b
        double upConstant = intsoffset[0];
        // 两个路口的相对绿波带宽
        double[] intsGreenWidths = new double[intssplit.length];

        // 计算所有路口的绿灯开始和结束时间
        for (int i = 0; i < intsoffset.length; i++) {
            int offset = intsoffset[i];  // 当前路口相位差
            int value = intssplit[i];   // 当前路口绿信比
            double greenStart = offset;

//            if (i == 0) {
//                greenStart = offset;
//            } else {
//                greenStart = intsGreenStartTime[i - 1] + intsdistances[i] / vel;
//            }
            double greenEnd = greenStart + value;

            // 上个路口开过来的车队到达时间
            double greenStartLastInts = 0;
            double greenEndLastInts = 0;
            if(i != 0){
                greenStartLastInts = ( intsGreenStartTime[i-1] + intsdistances[i] / vel ) % cycle;
                greenEndLastInts = greenStartLastInts + intssplit[i-1];
            }

            intsGreenStartTime[i] = greenStart;
            intsGreenEndTime[i] = greenEnd;

            int distance = 0;
            if (i != 0) {
                for (int j = 0; j <= i; j++) {
                    distance += intsdistances[j];
                }
            }
            // 计算上行到达起始时间
            intsUpArriveBeginTime[i] = distance / vel + upConstant;
            // 计算上行到达结束时间
            intsUpArriveEndTime[i] = intsUpArriveBeginTime[i] + intsGreenEndTime[0];
            // 计算上个路口到本路口的相对绿波带宽
            if (i != 0) {
                double greenStartBtw = Math.max( greenStartLastInts,greenStart );
                double greenEndBtw = Math.min( greenEndLastInts, greenEnd );
                if( greenStartBtw > cycle && greenEndBtw < cycle )
                    greenStartBtw = greenStartBtw-cycle;
                intsGreenWidths[i] = Math.max(greenEndBtw-greenStartBtw,0);
            }
        }

        // 计算下界
        // 下界在每一个路口的绿灯开始时间之后
        int start = intsoffset[0];
        for (int i = 1; i < intsUpArriveBeginTime.length; i++) {
            // 到达当前路口时，当前路口还是红灯
            double arriveTime = (double) Math.round(intsUpArriveBeginTime[i] * 100) /100;
            double greenStartTime = (double) Math.round(intsGreenStartTime[i] * 100) /100;
            if (arriveTime == greenStartTime) continue;
            double distance = 0;
            if (i != 0) {
                for (int j = 0; j <= i; j++) {
                    distance += intsdistances[j];
                }
            }
            if (arriveTime < greenStartTime) {
                // 计算b，最终计算出一个最大的b
                int lowerBound = (int) (greenStartTime - distance / vel);
                if (lowerBound > start) {
                    start = lowerBound;
                }
            }
        }
        // 计算上界
        // 上界在每一个路口的绿灯结束时间之前
        double end = intsGreenEndTime[0];
        for (int i = 1; i < intsUpArriveEndTime.length; i++) {
            // 到达当前路口时，当前路口还是红灯
            double arriveTime = (double) Math.round(intsUpArriveEndTime[i] * 100) /100;
            double greenEndTime = (double) Math.round(intsGreenEndTime[i] * 100) /100;
            if (arriveTime == greenEndTime) continue;
            double distance = 0;
            if (i != 0) {
                for (int j = 0; j <= i; j++) {
                    distance += intsdistances[j];
                }
            }
            if (arriveTime > greenEndTime) {
                // 计算b，最终计算出一个最大的b
                int upperBound = (int) (greenEndTime - distance / vel);
                if (upperBound < end) {
                    end = upperBound;
                }
            }
        }
        int widthTotal = 0;
        for(double greenwidth :intsGreenWidths){
            widthTotal += greenwidth;
        }
        int widthmin = (int) end - start;
        int width = 0;
        if(intsGreenWidths.length-1 > 0)            
            width = widthTotal/ (intsGreenWidths.length-1); // 排除第一个路口，第一个路口无绿波带宽

        int vehicle = width / 3;

        gwup.start = start;
        gwup.widthmin = widthmin;
        gwup.width = width;
        gwup.vehicle = vehicle;

    }

    /**
     * 计算下行带宽
     *
     * @param intsveldown
     * @param intssplitdown
     * @param intsoffset
     * @param intsdistances
     * @param downDistance
     */
    public void calDownBandwidth(double intsveldown, int[] intssplitdown, int[] intsoffset, double[] intsdistances, int downDistance,int[] intscycle) {

        int cycle = intscycle[0];

        gwdown.distance = downDistance;
        gwdown.type = "down";
        gwdown.speed = intsveldown;

        for(int i = 1; i < intscycle.length; i++){
            if( cycle != intscycle[i] )
                return;
        }

        double vel = intsveldown / 3.6; // 速度 m/s
        // 计算各路口的绿灯开始时间和结束时间
        // 各路口的绿灯开始时间
        double intsGreenStartTime[] = new double[intssplitdown.length];
        // 各路口的绿灯结束时间
        double intsGreenEndTime[] = new double[intssplitdown.length];
        // 下行：从最后一个路口绿灯开始-到达每个路口的时间
        double intsDownArriveBeginTime[] = new double[intssplitdown.length];
        // 下行：从最后一个路口绿灯结束-到达每个路口的时间
        double intsDownArriveEndTime[] = new double[intssplitdown.length];
        // x: 距离   y: 时间(考虑相位差)    y = x/v + b
        // 下行常量b
        double downConstant = intsoffset[intsoffset.length - 1];
        // 计算所有路口的绿灯开始和结束时间
        // 两个路口的相对绿波带宽
        double[] intsGreenWidths = new double[intssplitdown.length];

        for (int i = intssplitdown.length - 1; i >= 0; i--) {
            int offset = intsoffset[i];  // 当前路口相位差
            int value = intssplitdown[i];   // 当前路口绿信比
            double greenStart= offset;
            int distance = 0;
            if (i != intssplitdown.length - 1) {
                for (int j = intssplitdown.length - 1; j > i; j--) {
                    distance += intsdistances[j];
                }
            }
//            if (i == intssplitdown.length - 1) {
//                greenStart = offset;
//            } else {
//                greenStart = intsGreenStartTime[intsGreenStartTime.length - 1] + distance / vel;
//            }
            double greenEnd = greenStart + value;

            // 上个路口开过来的车队到达时间
            double greenStartLastInts = 0;
            double greenEndLastInts = 0;
            if(i != intssplitdown.length - 1){
                greenStartLastInts = (intsGreenStartTime[i+1] + intsdistances[i+1] / vel ) % cycle;
                greenEndLastInts = greenStartLastInts + intssplitdown[i+1];
            }

            intsGreenStartTime[i] = greenStart;
            intsGreenEndTime[i] = greenEnd;

            // 计算下行到达时间
            intsDownArriveBeginTime[i] = distance / vel + downConstant;
            intsDownArriveEndTime[i] = intsDownArriveBeginTime[i] + intssplitdown[intssplitdown.length - 1];
            // 计算上个路口到本路口的相对绿波带宽
            if (i != intssplitdown.length - 1) {
                double greenStartBtw = Math.max( greenStartLastInts,greenStart );
                double greenEndBtw = Math.min( greenEndLastInts, greenEnd );
                intsGreenWidths[i] = Math.max(greenEndBtw-greenStartBtw,0);
            }
        }
        // 计算下界
        // 下界在每一个路口的绿灯开始时间之后
        int start = (int) intsGreenStartTime[intsGreenStartTime.length - 1];
        for (int i = intsGreenStartTime.length - 2; i >= 0; i--) {
            // 到达当前路口时，当前路口还是红灯
            double arriveTime = intsDownArriveBeginTime[i];
            double greenStartTime = intsGreenStartTime[i];
            if (arriveTime == greenStartTime) continue;
            if (arriveTime < greenStartTime) {
                int distance = 0;
                if (i != intssplitdown.length - 1) {
                    for (int j = intssplitdown.length - 1; j > i; j--) {
                        distance += intsdistances[j];
                    }
                }
                // 计算b，最终计算出一个最大的b
                int lowerBound = (int) (greenStartTime - distance / vel);
                if (lowerBound > start) {
                    start = lowerBound;
                }
            }
        }
        // 计算上界
        // 上界在每一个路口的绿灯结束时间之前
        int end = (int) intsGreenEndTime[intsGreenEndTime.length - 1];
        for (int i = intsGreenStartTime.length - 2; i >= 0; i--) {
            // 到达当前路口时，当前路口还是红灯
            double arriveTime = intsDownArriveEndTime[i];
            double greenEndTime = intsGreenEndTime[i];
            if (arriveTime == greenEndTime) continue;
            if (arriveTime > greenEndTime) {
                int distance = 0;
                if (i != intssplitdown.length - 1) {
                    for (int j = intssplitdown.length - 1; j > i; j--) {
                        distance += intsdistances[j];
                    }
                }
                // 计算b，最终计算出一个最大的b
                int upperBound = (int) (greenEndTime - distance / vel);
                if (upperBound < end) {
                    end = upperBound;
                }
            }
        }
        int widthTotal = 0;
        for(double greenwidth :intsGreenWidths){
            widthTotal += greenwidth;
        }
        int widthmin = (int) end - start;
        int width = 0;
        if( intsGreenWidths.length-1 > 0 )
            width = widthTotal/ (intsGreenWidths.length-1); // 排除第一个路口，第一个路口无绿波带宽
        int vehicle = width / 3;

        gwdown.start = 0;
        gwdown.widthmin = widthmin;
        gwdown.width = width;
        gwdown.vehicle = vehicle;
    }
    /**
     * 获取干线协调带宽
     * @param type 计算带宽类型，green - 绿波带宽， red - 红波带宽
     * @param intsvelup 上行速度
     * @param intsveldown 下行速度
     * @param devs 路口参数
     * @return
     */
    public List<Greenwave> getBandWidth(String type, String direction, double intsvelup, double intsveldown,  List<RouteIntsection> devs, int[] intsoffset,String keyAgentid) {

        type = "green";

//        int intsoffset[] = new int[devs.size()]; // 保存优化后的相位差
        int intssplitup[] = new int[devs.size()]; // 保存路口协调相位绿信比的值
        int intssplitdown[] = new int[devs.size()]; // 保存路口协调相位绿信比的值

        int intscycle[] = new int[devs.size()]; // 保存协调路口方案的周期
        double intsdistances[] = new double[devs.size()]; // 保存各路口的距离
        double intslenthup[] = new double[devs.size()]; //保存上行路口长度的值
        double intslenthdown[] = new double[devs.size()]; //保存下行路口长度的值

        int[] intsoffsetUp = new int[devs.size()];
        int[] intsoffsetDown = new int[devs.size()];

//        int keyCycle = 0;

        // 得到关键路口的周期
//        for (RouteIntsection device : devs) {
//            if( keyAgentid.equals( device.getAgentid() ) ){
//                keyCycle = device.getFeature().getPatternList().get(0).getCycle();
//                break;
//            }
//        }
//        setAftcycle(keyCycle);
        int lastIntsWidth = 0;
        int upDistance = 0;
        int downDistance = 0;
        // 初始化各路口协调相位绿信比
        for (RouteIntsection dev : devs) {
            // 计算上下行距离
            if (dev.getSortid() != devs.size()) {
                upDistance += dev.getDistance() + dev.getWidth();
            } else {
                upDistance += dev.getDistance();
            }
            if (dev.getSortid() != 1) {
                downDistance += dev.getDistance() + dev.getWidth();
            } else {
                downDistance += dev.getDistance();
            }
            Pattern pattern = dev.getFeature().getPatternList().get(0);

            // 保存周期
            intscycle[dev.getSortid() - 1] = pattern.getCycle();

            // 保存各路口相位差
//            intsoffset[dev.getSortid() - 1] = dev.getFeature().getPatternList().get(0).getOffset();
            // 保存各路口距离
            intsdistances[dev.getSortid() - 1] = dev.getDistance();
            // 协调相位号
            int phasenoUp;
            int phasenoDown;
            // 阶段模式
            if( pattern.getRings() == null || pattern.getRings().isEmpty() || "stage".equals(pattern.getContrloType())){
                phasenoUp = getStageNumFromPhase( pattern, dev.getForwardphaseid() );
                phasenoDown = getStageNumFromPhase( pattern, dev.getBackphaseid() );
            }else{
                phasenoUp = dev.getForwardphaseid();
                phasenoDown = dev.getBackphaseid();
            }

            // 距离默认为路口中心点之间的距离；如果设置了路口宽度，则距离应该是路口之间的路段距离
            // 得到上行距离
            intslenthup[dev.getSortid() - 1] = dev.getDistance() + lastIntsWidth;
            // 得到下行距离
            intslenthdown[dev.getSortid() - 1] = dev.getDistance() + dev.getWidth();
            // 保存上一个路口的宽度，用于计算上行时下个路口的距离
            lastIntsWidth = dev.getWidth();

            List<List<Split>> rings = getSafeRingFromPattern(pattern);
            int offsetFromHead = 0; // 相位差在方案中的偏移
            // 获取参与协调相位的绿信比
            label:
            for (int i = 0; i < rings.size(); i++) {
                offsetFromHead = 0;
                for (Split split : rings.get(i)) {
                    if (phasenoUp == split.getId()) {
                        intssplitup[dev.getSortid() - 1] = split.getValue();
                        break label;
                    }
                    offsetFromHead += split.getValue();
                }
            }
            intsoffsetUp[dev.getSortid() - 1] = intsoffset[dev.getSortid() - 1] + offsetFromHead;

            label:
            for (int i = 0; i < rings.size(); i++) {
                offsetFromHead = 0;
                for (Split split : rings.get(i)) {
                    if (phasenoDown == split.getId()) {
                        intssplitdown[dev.getSortid() - 1] = split.getValue();
                        break label;
                    }
                    offsetFromHead += split.getValue();
                }
            }
            intsoffsetDown[dev.getSortid() - 1] = intsoffset[dev.getSortid() - 1] + offsetFromHead;

        }

        List<Greenwave> greenwaveList = new LinkedList<>();

        calUpBandwidth(intsvelup, intssplitup,intsoffsetUp,intslenthup,upDistance,intscycle);
        greenwaveList.add(getGwup());

        calDownBandwidth(intsveldown, intssplitdown,intsoffsetDown,intslenthdown,downDistance,intscycle);
        greenwaveList.add(getGwdown());

        return greenwaveList;
    }

    // 根据协调相位ID，找到对应的阶段ID
    public static int getStageNumFromPhase(Pattern pattern, int forwardphaseid) {
        int stageNum = 0;

        for( StageForStageMode stage : pattern.getStagesList() ){
            for( int phaseid :stage.getPhases() ){
                if( phaseid == forwardphaseid ) return stage.getKey()+1;
            }
        }

        return stageNum;
    }

    // 获取环信息，阶段方案自动转环
    public static List<List<Split>> getSafeRingFromPattern(Pattern pattern) {
        List<List<Split>> rings;
        // 兼容阶段模式
        if (pattern.getRings() == null || pattern.getRings().isEmpty() || "stage".equals(pattern.getContrloType())) {
            List<Split> splits = new ArrayList<>();
            for (StageForStageMode stage : pattern.getStagesList()) {
                Split split = new Split(stage.getKey() + 1, stage.getStageSplit(), 0);
                splits.add(split);
            }
            rings = new ArrayList<>();
            rings.add(splits);
            pattern.setContrloType("stage");
            pattern.setRings(rings);

        } else
            rings = pattern.getRings();

        return rings;
    }
}
